import SignerBase from './SignerBase'
import { formatUTCDate, getQueryObject, getSortedParams } from '../tools/utils'

const hmacSHA256 = require('crypto-js/hmac-sha256')
const sha256 = require('crypto-js/sha256')
export default class AuthorizationHeaderSigner extends SignerBase {
  /// <summary>
  /// Computes an AWS4 signature for a request, ready for inclusion as an
  /// 'Authorization' header.
  /// </summary>
  /// <param name="headers">
  /// The request headers; 'Host' and 'X-Amz-Date' will be added to this set.
  /// </param>
  /// <param name="queryParameters">
  /// Any query parameters that will be added to the endpoint. The parameters
  /// should be specified in canonical format.
  /// </param>
  /// <param name="bodyHash">
  /// Precomputed SHA256 hash of the request body content; this value should also
  /// be set as the header 'X-Amz-Content-SHA256' for non-streaming uploads.
  /// </param>
  /// <param name="awsAccessKey">
  /// The user's AWS Access Key.
  /// </param>
  /// <param name="awsSecretKey">
  /// The user's AWS Secret Key.
  /// </param>
  /// <returns>
  /// The computed authorization string for the request. This value needs to be set as the
  /// header 'Authorization' on the subsequent HTTP request.
  /// </returns>
  public ComputeSignature(
    headers: Map<string, string>,
    queryParameters: string,
    bodyHash: string,
    awsAccessKey: string,
    awsSecretKey: string
  ): string {
    // first get the date and time for the subsequent request, and convert to ISO 8601 format
    // for use in signature generation
    let requestDateTime = new Date(Date.UTC(2013, 4, 24)) // new Date()
    let dateTimeStamp = formatUTCDate(requestDateTime, this.ISO8601BasicFormat)

    headers.set(this.X_Amz_Date, dateTimeStamp)
    // extract the host portion of the endpoint to include in the signature calculation,
    // unless already set
    if (!headers.has('Host')) {
      let hostHeader = this.EndpointUri.host
      if (this.EndpointUri.port) hostHeader += ':' + this.EndpointUri.port
      headers.set('Host', hostHeader)
    }

    let dateStamp = formatUTCDate(requestDateTime, this.DateStringFormat)
    let scope = `${dateStamp}/${this.Region}/${this.Service}/${this.TERMINATOR}`

    // canonicalized headers need to be expressed in the query
    // parameters processed in the signature
    let canonicalizedHeaderNames = this.CanonicalizeHeaderNames(headers)
    let canonicalizedHeaders = this.CanonicalizeHeaders(headers)

    // reform the query parameters to (a) add the parameters required for
    // Signature V4 and (b) canonicalize the set before they go into the
    // signature calculation. Note that this assumes parameter names and
    // values added outside this routine are already url encoded
    let paramDictionary = new Map<string, string>()
    if (queryParameters) {
      paramDictionary = getQueryObject(queryParameters)
    }

    // build the expanded canonical query parameter string that will go into the
    // signature computation
    let canonicalizedQueryParameters = getSortedParams(paramDictionary)

    // express all the header and query parameter data as a canonical request string
    let canonicalRequest = this.CanonicalizeRequest(
      this.EndpointUri,
      this.HttpMethod,
      canonicalizedQueryParameters,
      canonicalizedHeaderNames,
      canonicalizedHeaders,
      bodyHash
    )
    console.log('\nCanonicalRequest:\n', canonicalRequest)

    // construct the string to be signed
    let stringToSign = `${this.SCHEME}-${this.ALGORITHM}
${dateTimeStamp}
${scope}
${sha256(canonicalRequest)}` // canonicalRequest need encode

    console.log('\nStringToSign:\n', stringToSign)

    // compute the multi-stage signing key
    let key = this.DeriveSigningKey(awsSecretKey, dateStamp)
    // compute the final signature for the request, place into the result and return to the
    // user to be embedded in the request as needed
    let signatureString = hmacSHA256(stringToSign, key).toString()
    console.log('\nSignature:\n', signatureString)

    const authorization = `${this.SCHEME}-${this.ALGORITHM} Credential=${awsAccessKey}/${scope}, SignedHeaders=${canonicalizedHeaderNames}, Signature=${signatureString}`

    console.log('\nAuthorization:\n{0}', authorization)

    return authorization
  }
}
